package com.smmisia.shop.job;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.smmisia.shop.constant.TradeOrderState;
import com.smmisia.shop.constant.UserType;
import com.smmisia.shop.entity.TradeOrder;
import com.smmisia.shop.mapper.TradeOrderMapper;
import com.smmisia.shop.request.OrderTimeoutRequest;
import com.smmisia.shop.service.TicketOrderService;
import com.smmisia.shop.service.TradeOrderService;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.LinkedBlockingQueue;

@Component
@Slf4j
public class OrderJob {

    @Autowired
    private TradeOrderMapper orderMapper;

    @Autowired
    private TradeOrderService tradeOrderService;

    private static final int CAPACITY = 2000;

    private final BlockingQueue<TradeOrder> orderTimeoutBlockingQueue = new LinkedBlockingQueue<>(CAPACITY);

    private final ForkJoinPool forkJoinPool = new ForkJoinPool(10);

    private static int MAX_TAIL_NUMBER = 99;

    private static final TradeOrder POISON = new TradeOrder();

    @XxlJob("orderTimeOutExecute")
    public ReturnT<String> orderTimeOutExecute() {
        log.info("订单超时处理开始");
        try {
            int shardIndex = XxlJobHelper.getShardIndex();
            int shardTotal = XxlJobHelper.getShardTotal();

            log.info("orderTimeOutExecute start to execute , shardIndex is {} , shardTotal is {}", shardIndex, shardTotal);

            List<String> buyerIdTailNumberList = new ArrayList<>();
            for (int i = 0; i <= MAX_TAIL_NUMBER; i++) {
                if (i % shardTotal == shardIndex) {
                    buyerIdTailNumberList.add(StringUtils.leftPad(String.valueOf(i), 2, "0"));
                }
            }

            buyerIdTailNumberList.forEach(buyerIdTailNumber -> {
                try {
                    //TODO 分页查询
                    List<TradeOrder> orderList = orderMapper.selectList(new QueryWrapper<TradeOrder>()
                            .in("order_state", TradeOrderState.CONFIRM.name(), TradeOrderState.CREATE.name())
                            .lt("gmt_create", DateUtils.addMinutes(new Date(), -TradeOrder.DEFAULT_TIME_OUT_MINUTES))
                            .likeRight("reverse_buyer_id", buyerIdTailNumber)
                            .orderBy(true, true, "gmt_create")
                    );
                    orderTimeoutBlockingQueue.addAll(orderList);
                    forkJoinPool.execute(this::executeTimeout);
                } finally {
                    orderTimeoutBlockingQueue.add(POISON);
                    log.debug("POISON added to blocking queue ，buyerIdTailNumber is {}", buyerIdTailNumber);
                }
            });

            return ReturnT.SUCCESS;
        } catch (Exception e) {
            log.error("orderTimeOutExecute failed", e);
            throw e;
        }
    }

    private void executeTimeout() {
        TradeOrder tradeOrder = null;
        try {
            while (true) {
                tradeOrder = orderTimeoutBlockingQueue.take();
                if (tradeOrder == POISON) {
                    log.debug("POISON toked from blocking queue");
                    break;
                }
                log.info("executeTimeout tradeOrderId = {}" , tradeOrder.getId());
                executeTimeoutSingle(tradeOrder);
            }
        } catch (InterruptedException e) {
            log.error("executeTimeout failed", e);
        }
        log.debug("executeTimeout finish");
    }

    public void executeTimeoutSingle (TradeOrder order){
            log.info("订单超时，订单号:{}", order.getOrderId());
            OrderTimeoutRequest orderTimeoutRequest = new OrderTimeoutRequest();
            orderTimeoutRequest.setOrderId(order.getOrderId());
            orderTimeoutRequest.setOperateTime(new Date());
            orderTimeoutRequest.setOperator(UserType.PLATFORM.name());
            orderTimeoutRequest.setOperatorType(UserType.PLATFORM.name());
            orderTimeoutRequest.setIdentifier(order.getOrderId());
        tradeOrderService.SendMessageForClose(orderTimeoutRequest);
    }
}
